#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :日历.py
# @Time      :2025/6/11 18:37
# @Author    :Ethon

import time
import random
from lxml import etree
import requests
import pandas as pd
import datetime
import os
import sys

pd.set_option('display.width', None)


# 定义装饰器来处理权限错误
def handle_permission_error(func):
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except PermissionError as e:
            print(f"无法写入文件 '{args[1]}日历.csv'，因为它正被其他程序占用。")
            print("请关闭文件后再试。")
            sys.exit(1)  # 退出程序

    return wrapper


class BingCalendar():
    def __init__(self):
        self.headers = {
            "accept": "*/*",
            "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
            "cache-control": "no-cache",
            "ect": "4g",
            "pragma": "no-cache",
            "priority": "u=1, i",
            "referer": "https://cn.bing.com/search?q=%E6%97%A5%E5%8E%86&form=ANNTH1&refig=68495a947e5346bc8da65a3c65669e94&pc=CNNDDB&adppc=EdgeStart&pq=ril&pqlth=3&assgl=2&sgcn=%E6%97%A5%E5%8E%86&qs=SC&sgtpv=SC&smvpcn=0&swbcn=10&sctcn=0&sc=10-3&sp=1&ghc=0&cvid=68495a947e5346bc8da65a3c65669e94&clckatsg=1&hsmssg=0",
            "sec-ch-ua": "\"Microsoft Edge\";v=\"137\", \"Chromium\";v=\"137\", \"Not/A)Brand\";v=\"24\"",
            "sec-ch-ua-arch": "\"x86\"",
            "sec-ch-ua-bitness": "\"64\"",
            "sec-ch-ua-full-version": "\"137.0.3296.68\"",
            "sec-ch-ua-full-version-list": "\"Microsoft Edge\";v=\"137.0.3296.68\", \"Chromium\";v=\"137.0.7151.69\", \"Not/A)Brand\";v=\"24.0.0.0\"",
            "sec-ch-ua-mobile": "?0",
            "sec-ch-ua-model": "\"\"",
            "sec-ch-ua-platform": "\"Windows\"",
            "sec-ch-ua-platform-version": "\"19.0.0\"",
            "sec-fetch-dest": "empty",
            "sec-fetch-mode": "cors",
            "sec-fetch-site": "same-origin",
            "sec-ms-gec": "0A362E4759E509C82F1AE7EAE00E910B1CA1637DD64D951B868A3A3D1E199461",
            "sec-ms-gec-version": "1-137.0.3296.68",
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0",
            "x-client-data": "eyIxIjoiMjAiLCIyIjoiMSIsIjMiOiIwIiwiNCI6Ii0yNzIxOTQ0NDA3MTY3NDI4NzU1IiwiNiI6InN0YWJsZSIsIjkiOiJkZXNrdG9wIn0=",
            "x-edge-shopping-flag": "0"
        }
        self.url = "https://cn.bing.com/richcalendar/fetch"

    def fetch_data(self, year, month, day):
        '''
        获取指定日期的日历数据
        :param year:  年份
        :param month: 月份
        :param day:  日期
        :return: 当天数据，当月数据
        '''
        params = {
            "year": str(year),
            "month": str(month),
            "day": str(day),
            "ishol": "false",
            "IG": "62F740898CEF487399CC61362FB399AD",  # 修改月份不变化
            "changeYear": "false",
            "IID": "SERP.5608",
            "ensearch": "0"
        }
        response = requests.get(self.url, headers=self.headers, params=params)
        date_data = self.parse_data(response.text)
        return date_data

    def parse_data(self, text):
        tree = etree.HTML(text)
        rcld_dayweek = tree.xpath("//div[@class='rcld_dayweek']/div/text()")
        today_dict = {}
        rcld_week = rcld_dayweek[0]  # 星期几
        rcld_rday = rcld_dayweek[1]  # 几号
        rcld_rdate = tree.xpath("//div[@class='rcld_rbase']/div[1]/text()")[0]  # 年月
        rcld_festivals = tree.xpath("////div[@id='rcld_festivals']/div/text()")  # 节日
        today_dict['星期'] = rcld_week
        today_dict['年月'] = rcld_rdate
        today_dict['日'] = rcld_rday
        today_dict['节日'] = rcld_festivals
        # print(today_dict)

        rcld_calendar = tree.xpath("//div[@class='rcld_calendar']/div")
        rcld_th = rcld_calendar[0].xpath("./div/div/text()")
        date_list = []
        for rcld_row in rcld_calendar[1:]:
            for i in range(len(rcld_row.xpath('./div'))):
                div = rcld_row.xpath('./div')[i]
                div_dict = {}
                div_dict['日期'] = div.xpath("./@data-date")[0]
                div_dict['星期'] = rcld_th[i]
                div_dict['日 '] = div.xpath("./@data-day")[0]
                div_dict['日链接'] = div.xpath("./@data-msnurl")[0]  # 当天日历跳转链接
                div_dict['日历标签'] = div.xpath("./@data-datetagsstr")[0]  # 日历标签
                div_dict['宜忌'] = div.xpath("./@data-rileft")[0]  # 宜忌
                try:
                    div_dict['节日'] = div.xpath("./@data-festivals")[0]
                except Exception as e:
                    div_dict['节日'] = '-'
                try:
                    div_dict['节假日'] = div.xpath("./a/div[@class='rcld_celllabel']/text()")[0]
                except Exception as e:
                    div_dict['节假日'] = '-'
                div_dict['农历'] = div.xpath("./a/div[@class='rcld_cellalias ']/text()")[0]
                date_list.append(div_dict)
        date_data = pd.DataFrame(date_list)
        # print(date_data)
        return today_dict, date_data

    @handle_permission_error  # 应用装饰器
    # 清洗数据
    def clean_data(self, year):
        df = pd.read_csv(f'{year}日历.csv')
        # 按日期对表格去重
        df = df.drop_duplicates(subset=['日期'], keep='first')
        # 清除掉日期中年份不为year的行
        df = df[df['日期'].str.contains(str(year))]
        # 重置行索引
        df = df.reset_index(drop=True)
        df.to_csv(f'{year}日历.csv', index=False)
        # print(df)
        return df

    @handle_permission_error
    def get_all_year(self, year):
        dates_list = []

        # 遍历1到12月
        for month in range(1, 13):
            # 创建每月1号的日期对象
            dates_list.append([year, month, 1])
        file_path = f'{year}日历.csv'
        for dates in dates_list:
            year, month, day = dates
            today_dict, date_data = self.fetch_data(year, month, day)
            print('正在获取{}年{}月的数据'.format(year, month))
            # print(date_data)
            date_data.to_csv(file_path, mode='a', header=not os.path.exists(file_path), index=False)
            time.sleep(random.randint(1, 3))

        # 清洗数据
        all_date_data = self.clean_data(year)
        return all_date_data


if __name__ == '__main__':
    bc = BingCalendar()

    '''指定日期及当月日历'''
    # year, month, day = 2025, 6, 15
    # today_dict, date_data = bc.fetch_data(year, month, day)  # 填入年月日返回当天数据及当月数据
    # print(today_dict)
    # print(date_data)

    '''获取全年日历数据'''
    year = 2025
    all_date_data = bc.get_all_year(year)  # 运行一次就会保存到相应年份的csv文件里面
    print(all_date_data)

    '''测试清洗数据'''
    # year = '2025'
    # clean_data = bc.clean_data(year)
